Python 教学 | Pandas 表格字段类型精讲(含类型转换)
Python教学专栏,旨在为初学者提供系统、全面的Python编程学习体验。通过逐步讲解Python基础语言和编程逻辑,结合实操案例,让小白也能轻松搞懂Python!
>>>点击此处查看往期Python教学内容
本文目录
一、前言
二、再谈数据类型
(一)字段类型错误有何影响
(二)Pandas中表格初始字段类型从何而来
三、Pandas中关于字段类型的一些“坑
四、字段类型转换
五、总结
六、Python教程
本文共9230个字,阅读大约需要24分钟,欢迎指正!
Part1前言
在使用 Python 处理数据过程中,或者在使用(匹配、入库……)数据之前,我们需要为表格数据的每个字段分配好合适的类型,这样才能高效地进行数据运算并且保证字段含义无误。在 Pandas 系列的第一期文章数据处理必备工具之 Pandas(基础篇)中,我们简单说明了 Pandas 中的表格类型 DataFrame(数据框)中每个字段(列)都有自己的类型,例如整数型、浮点数型、日期类型等。而本期文章,我们将在字段类型这一话题上进行更加深入的探索,同时学习如何对数据字段做类型转换。
💡后台回复关键词“20230616”即可获取本文所有演示代码以及演示用的数据。
Part2再谈数据类型
1 字段类型错误有何影响
在介绍 Pandas 的第一期基础篇文章中,我们已经简单说明了在表格数据 DataFrame 类型中,除了某一个单元格内的数据值,表格的一个字段(即一列)也是有类型属性的,常见的字段类型有 int
、float
、dateime
、object
等。在我们使用的统计数据或面板数据中,一般情况下一列中会存储含义相同的数据值,例如“年份”字段中一般存放四位整数,所以在 Pandas 中字段类型最好是int
;“日期”字段中则一般存放日期数据,那么字段类型理应是dateime
。
那么如果字段类型有误,会带来什么影响呢?首先是数据含义不对版,例如数据中有一字段“年份”,内容都是2022、2023等四位数字,那么这个字段的类型应该是int
,但如果不小心搞成了float
型(浮点数型),那么其中的数据值就会变成 2022.0、2023.0,如此一来虽然数据值不变,但是加上小数点后数值的含义还是受到了影响。另外,错误的字段类型还会影响数据的运算和匹配等操作,如果数据中“日期”字段如果不是日期类型dateime
,而是字符类型string
或混合类型object
,那么当我们想要根据日期来筛选某一时间段的数据时,是无法根据数据值去做计算的,类似的影响还存在于数据匹配之中,两份数据做匹配,但如果ID字段类型不一致,最后可能也会造成匹配错误。
2Pandas 中表格初始字段类型从何而来
我们在使用 Pandas 做数据处理时,一般还没有特意分配字段类型,数据就已经自动具备了相对合适的类型,那么数据初始的字段类型是怎么来的呢?这个问题要分情况解答,首先,如果表格数据是我们自己使用pd.DataFrame()
函数生成的,我们在生成数据时,可以使用参数dtype
手动设置字段的类型(仅支持将所有字段设置为同一类型),此时系统尽可能将满足条件的字段设置为指定类型,下面是一个简单的例子。
import pandas as pd # 导入 pandas
# 创建数据
df = pd.DataFrame([[2008, '北京'],
[2012, '伦敦'],
[2016, '里约热内卢'],
[2020, '东京'], # 使用 dtype 参数来设置所有字段的类型
[2024, '巴黎'],], columns=['年份', '奥运承办城市'], dtype='int64')
df.dtypes # 查看表格 df 中各字段类型
# 下面是返回结果
'''
年份 int64
奥运承办城市 object
'''
上述代码创建了一个表格,共两个字段:年份
和奥运承办城市
,笔者尝试将所有字段都设置为int64
类型,不过数据创建后,发现只有年份字段的类型是指定的类型,这是因为指定类型只会将数据值符合要求的数据值设置为指定类型,例如年份字段中数据值都是整数,就可以被设置为int64
类型,而奥运承办城市字段主要是字符数据,不满足int64
类型的要求(一个字段中只要有一个数据值不符合要求,即不满足),所以这个字段类型是由 Pandas 系统自动推测分配的。
💡 小知识:
Python 中的整数类型和浮点数类型也有细分类型,例如int
型主要包括 int16
、int32
、int64
等;float
型则主要包括 float16
、float32
、float64
。不同类型类型占用不同的位数和精度,例如int16
能够表示的最大整数范围仅为 -32768 到 32767,而int32
则可以表示的整数范围是 -2147483648 到 2147483647,int64能够表示的整数值范围更大,达到 19 位整数。与之类似,float16
能表示的浮点数仅支持到小数点后 3 位,float32
支持小数点后 7 位,float64
最大则支持小数点后 15 位,不仅如此,它们存储的浮点数的整数部分也有区分,总的来说,精度越大,能够保存的数值范围也就越广。一般来说(不包括pd.DataFrame()
函数,具体可看下文),Pandas 中使用的数值类型大多都是 64 位精度,基本能够满足需求。我们在设置字段类型时,可以设置位数或精度,也可以直接将类型指定为int
,如果使用pd.DataFrame()
函数来创建数据表并且设置类型为int
,那么这里的 int 将会是 int32,如果数据中存在整数超出了int32的最大范围,那么数值将会发生变化,所以需要设置整数类型时,最好设置参数dtype='int64'
;如果是浮点数的话,函数默认使用的是float64,所以直接设置类型为float
即可,不必特意指定精度。
前面说了创建表格并且指定字段类型的情况,如果我们在使用pd.DataFrame()
函数创建表格时不指定字段类型会如何呢?答案是 Pandas 会自动根据字段的数据值来推测和设置字段的类型。如果一个字段中的数据值都是整数,Pandas 会自动为其分配整数型(使用pd.DataFrame()
的话,默认的是int32
),如果一个字段中存在字符型数据或者字段中数据值的类型不唯一,那么就会被分配混合类型object
。
除了使用 Pandas 创建表格,做数据处理最常用的就是从已有的文件中读取数据,在社科类数据中,常见的有 excel 工作表,csv 文件,Stata表格文件(dta数据)。其中 excel 工作表和 Stata 表格文件这两种表格数据本身就具有类型属性,使用 Pandas 读取时,会自动地继承原有数据的类型并转为 Python 的字段类型,例如 Excel 表中的“数值”类型会自动转为int64
或float64
。如果 Excel 表一列中数据的类型不一致,Pandas 也会继承每个单元格中数据的类型,最后这个字段的类型大概率是混合类型object
。在读取 Excel 表时,如果我们不想使用表中原本的类型,而是希望重新设置字段类型,那么在读取时可以通过设置pd.read_excel()
函数的dtype
参数来控制字段类型,与pd.DataFrame()
不同的是,pd.read_excel()
函数中的dtype
参数支持主动为不同字段分配类型。
df = pd.read_excel('./Excel表.xlsx',
dtype={'资产总计(万元)':'float', '行业门类名称':'string'})
例如在上述代码中就可以使用dtype
来将资产总计(万元)
字段的类型设置为float
,行业门类名称
字段的类型设置为string
。除了上述用法之外也可以设置参数dtype=str
来将表中所有可以设置为字符型的数据值设置为字符型(空值除外),如果是从 csv 文件中读取表格,情况就又不一样了,因为 csv 是纯文本文件,无法保存数据值的类型,所以和读取 Excel 表类似,也可以通过设置参数dtype
来设置字段的类型,由于 csv 表格没有数据类型,所以没有被设置类型的字段都将由 Pandas 根据字段中的数据值推测和设置字段的类型,如果在读取时没有设置字段类型,那么所有字段的类型都将由 Pandas 自动推测设置。
Part3Pandas 中关于字段类型的一些“坑”
这一节我们讨论一下字段类型带给 Pandas 的一些“巨坑”。下面我们先熟悉本文演示用的数据(csv 格式数据表),为了方便大家了解使用不同工具对数据本身的影响,下面我们以三种不同的方式打开(或读取)演示数据。
下图是使用文本模式打开演示用的 csv 数据的截图。由于 csv 数据本就是文本文件,所以用这种方式打开 csv 数据的特色就是“原汁原味”,能够最真实地查看数据,所见即所得,缺点是不易观察表格结构,内容排列比较散乱(首行是表头,一行数据值之间使用逗号分隔开)。
注意观察上图,为了能够清晰的展示后续处理的要点,笔者刻意将数据中存在缺失值或异常值的数据行放在最前面。例如成立日期字段中日期的主要格式为年-月-日
,而第 1,3 行数据中日期的格式分别为年/月/日
和年 月 日
;此外,前几行数据中的缺失值和小数的位数也值得留意。
下图是使用 Excel / WPS 打开演示数据的截图。
成立日期
字段中,将格式为年-月-日
的文本信息推测并自动转换为日期类型,日期中的分隔符也由短横杠-
替换为斜杠/
,不过由于第三行中日期1998 12 08
类型不符合要求,并没有转为日期,继续保持字符类型(从数据值居左可以看出);除此之外,行业大类代码
字段中的数据值也被推测为数字类型,这一点可以看行业大类代码
字段第 2 行数据中,原本应该是0100
的数据值被转为整数100
。下面我们使用 Python 的 Pandas 库读取上述 csv 数据,读取时不主动设置字段类型,让 Pandas 自动推测数据类型,代码如下。
import pandas as pd # 导入 pandas 库,按照习惯起别名为 pd
# 读取演示(csv)数据,不指定数据字段类型
data = pd.read_csv('./CPPGD_清洁生产产业(核心)企业微观数据_部分字段样例数据1000条.csv')
# 输出各字段类型
print(data.dtypes)
"""
企业名称 object
成立日期 object
行业大类名称 object
行业大类代码 float64
注册资金(万元) float64
币种 object
行政区划代码 int64
"""
data # 查看读取后的数据
观察上图,正如笔者上文所说,读取 csv 数据时如不通过参数dtype
指定字段类型,Pandas 将会根据字段内的数据值推测并分配字段类型。通过上述代码中输出的字段类型和上图可知,由于注册资金(万元)
字段中的值都是数字,所以这个字段的类型变成了浮点数类型float64
,有一点值得注意,原始数据中(参考上文以文本方式打开数据的截图)该字段中原本存在很多整数,但是 Pandas 读取之后这些整数都变成了六位小数,这是因为字段中同时存在且仅存在整数和浮点数,由于浮点类型的精度比整型更高,为了避免数据精度丢失,Pandas 会自动将整型转换为浮点型,准确来说是让字段中所有值都向精度最高的值看齐,比如该字段中精度最高的值为第三行的176092.884369
,这是一个六位浮点数,那么字段中其他所有值都会被转换为六位浮点数。这是 Pandas 库中一个让人很无奈的设定。另外可以发现行业大类代码
字段的类型也被推测为float64
,明明这个字段中不存在浮点数,为什么还会变成被推测为浮点数类型呢?而且其中的数字都变成了一位小数。这是因为该字段中含有缺失值,由于在 Pandas 1.x 版本中只使用了 Numpy 库作为后台,所以读取表格数据时,如果存在缺失值,那么读取后默认使用 Numpy 库中的空值 NaN 代替(在代码中写作 numpy.nan 或 numpy.NaN,意为 Not a number)。这本身无可厚非,但是空值 NaN 是有类型的,它的类型是浮点数float
,按照上文的说法,为了避免数据精度丢失,Pandas 自动将字段中精度低的数值转为精度高的数值,这就导致整个字段中的值都变成了浮点数,同时,与使用 Excel 打开 csv 数据相似,第 2 行中数据值0100
也被推测为整数100
,只不过由于 Pandas 的字段精度特性,再次被转为浮点数100.0
。
正常情况下,如果 csv 数据一个字段中只存在整数,没有缺失值和浮点数,那么字段类型将会是整数型int64
,这一点可以参考上图中行政区划代码
字段。在上文中,我们提到正确的字段类型是数据含义的一个保障,例如在演示数据中,行业大类代码
和行政区划代码
两个字段中的数据值不应该是数字类型,而是字符类型,只有保存为字符类型才能准确无误的表示数据的含义,所以遇到类似的情况可以在读取数据时就主动设置这些字段的类型,当然上文的做法是一个真正的“反面教材”。
除了刚才提到的几个字段之外,其他几个字段的类型均是object
,上文有提到object
是一个混合字段类型,大概意思就是一个字段的类型如果是object
,说明该字段中存在不止一种类型的数据。不过当类型是object
时,除了字段内含有多种类型的数据,还有一种情况,那就是字段中存在字符类型数据,即使其中的数据值全部都是字符类型,那么在不主动转换或设置的情况下,这个字段的类型也将会是object
,而不是string
,不过这并不影响正常处理和使用数据。所以读取演示数据后其他字段的类型都是object,因为这些字段中都含有字符串,由此也能看出Pandas 读取 csv 数据时,不会主动将日期格式的文本推测为日期类型,而是保存为字符类型。
Part4字段类型转换
在使用 Pandas 对数据进行清洗,筛选,匹配,入库等操作时,可能需要中途改变一些字段的类型,那么这一节我们就来了解一下如何转换数据框DataFrame
的字段类型。
在 Pandas 中,astype()
函数是最通用,也是最常使用的类型转换函数,既可以一次性对整张表做类型转换,也可以对表中某个字段做类型转换,一般后者的使用场景较多。以上一节读取的演示数据为例,下面是将行政区划代码
字段的类型由整数型int64
转为字符型string
的代码。
print(data['行政区划代码'].dtype) # 输出转换前该字段的类型
# 转换字段类型为 string
data['行政区划代码'] = data['行政区划代码'].astype('string') # 或 data['行政区划代码'].astype('string', inplace=True)
print(data['行政区划代码'].dtype) # 输出转换后该字段的类型
# 输出值如下
'''
int64
string
'''
上述第二行代码即为转换类型为string
的代码,但不只这一种用法,我们还可以使用下面的代码来将类型转为字符型。
# 方法 2
data['行政区划代码'] = data['行政区划代码'].astype('str')
# 方法 3
data['行政区划代码'] = data['行政区划代码'].astype(str)
不过使用上述两种代码,转换后的字段类型将不是string
,而是object
。另外,转换数据类型的目的不是改变字段的类型,而是通过转换字段类型的方式改变这个字段内所有值的类型,这一点需要明确。
💡 有一点需要特别注意,当需要将一个字段的类型转为字符型时,需要特别注意该字段中是否含有空值 NaN
,None
或 pd.NA
。因为这些空值虽然不代表任何数据值,但是在类型转换过程中会将NaN
转换为字符串'nan'
,空值None
转换为字符串'None'
,空值pd.NA
转换为字符串'<NA>'
。如果希望字段转为字符类型的过程中将这些空值都转为空字符串''
,那么正确的做法是先填充字段中的缺失值,填充为空字符串''
,然后再转换类型,就不会出现问题了。
下面我们尝试将注册资金(万元)
字段的类型由浮点数类型float64
转为整数型,代码如下。
print(data['注册资金(万元)'].dtype) # 输出转换前该字段的类型
# 转换字段类型为 int, 在 astype() 中使用 int, 'int', 'int64' 均可
data['注册资金(万元)'] = data['注册资金(万元)'].astype(int) # 或 data['注册资金(万元)'].astype(int, inplace=True)
print(data['注册资金(万元)'].dtype) # 输出转换后该字段的类型
# 输出值如下
'''
float64
int32
'''
上述代码将含有浮点数的注册资金(万元)
字段转换为整数类型,这样做虽然能够达到类型转换的目的,但是字段中所有浮点数的小数部分都会被抹除,等于是直接修改了部分数据的值,所以转换类型时需要谨慎,避免类似情况出现。转换类型后的数据如下图所示。
下面我们再将行业大类名称
字段的类型转为整数型,由于这个字段中原本不含小数,所以不会改变字段内的数据值(这个字段本应在读取时就设置为字符型,这里只是为了做演示)。转换类型的代码如下。
# 转换字段类型为 int, 在 astype() 中使用 int, 'int', 'int64' 均可
data['行业大类代码'] = data['行业大类代码'].astype(int) # 或 data['行业大类代码'].astype(int, inplace=True)
运行上述代码,结果程序抛出异常:IntCastingNaNError: Cannot convert non-finite values (NA or inf) to integer
,这个异常告诉我们 Pandas 中的空值 NaN 不可以被转为整数,实际上正是如此,NaN 的类型是 float,缺失无法被转为整数型,所以转换不会成功,程序自然就会报错。
除了常规的数字型、字符型之间的转换,转换日期也是一个重点,转换为日期类型后,字段中的日期就可以被用来比较运算,进而满足我们需求。下面我们将演示数据中成立日期
字段由字符型转换为日期类型,代码如下。
print(data['成立日期'].dtype) # 输出转换前该字段的类型
# 转换字段类型为日期类型,或者说时间戳类型
data['成立日期'] = data['成立日期'].astype('datetime64[ns]')
print(data['成立日期'].dtype) # 输出转换后该字段的类型
# 输出值如下
'''
object
datetime64[ns]
'''
在 Pandas 中,使用astype('datetime64[ns]')
就可以将字段中日期类型的字符串转为时间戳,在成立日期字段中,笔者刻意设置了三种不同格式的日期,但是 Pandas 都会智能地将它们转换为日期。
上述转换为日期类型的代码中,使用到的参数值是'datetime64[ns]'
,这是一种精确到纳秒级别的时间戳类型,除了该类型之外,还存在其他精度的时间戳,例如datatime64[us]
表示微秒级别的时间戳,datatime64[h]
表示小时级别的时间戳等等。当然,对于社科类的微观数据来说,一般的时间戳只是精确到“日”,我们在转换时使用'datetime64[ns]'
就可以了。另外,如果转换前日期字段中存在空值,那么转换过程中这些空值会被转换为 Pandas 中专门用于表示空时间戳的pd.NaT
,这样做是为了保持数据类型一致。
最后,我们介绍一下读取上述数据的正确做法,由于行业大类名称
和行政区划代码
字段不应该是数字类型,所以我们在读取时就要设置它们的类型为字符型,代码如下。
## 读取演示(csv)数据,不指定数据字段类型
data = pd.read_csv('./CPPGD_清洁生产产业(核心)企业微观数据_部分字段样例数据1000条.csv',
dtype={'行业大类代码':'string', '行政区划代码':'string'}) # 将类型设置为 'str' 或 str 也可
print(data.dtypes)
# 输出值如下
'''
企业名称 object
成立日期 object
行业大类名称 object
行业大类代码 string
注册资金(万元) float64
币种 object
行政区划代码 string
'''
data # 输出查看数据
如上图所示,设置类型后,两字段的类型都变为string
,由于设置的类型是string
而不是str
或'str'
,所以字段内的空值也发生了变化(变为 pd.NA,而不是 NaN)。另外,注册资金(万元)
字段中存在的整数与浮点数不能共存的问题,我们将在后续的文章中继续深入探讨。
Part5总结
在 Pandas 中,字段类型看似是一个很小的细节,但笔者就曾因为没有深入了解其中的规则规律而吃了不小的亏,相信使用 Python 做数据处理的小伙伴多多少少也都遇到过这类问题。这也说明我们掌握这些知识其实是非常有必要的。本期文章到此结束,下期文章我们将继续分享 Pandas 的其他技巧。
Part6Python教程
Python教学 | 学习 Python 第一步——环境安装与配置 Python教学 | Python 基本数据类型 Python教学 | Python 字符串操作(上) Python教学 | Python 字符串操作(下) Python教学 | Python 变量与基本运算 Python教学 | 组合数据类型-列表 Python教学 | 组合数据类型-集合(内含实例) Python教学 | 组合数据类型 - 字典&元组 Python教学 | Python 中的分支结构(判断语句) Python教学 | Python 中的循环结构(上) Python教学 | Python 中的循环结构(下) Python教学 | Python函数的定义与调用 Python教学 | Python 内置函数 Python教学 | 最常用的标准库之一 —— os Python教学 | 盘点 Python 数据处理常用标准库 Python 教学 | “小白”友好型正则表达式教学(一) Python 教学 | “小白”友好型正则表达式教学(二) Python 教学 | “小白”友好型正则表达式教学(三) Python 教学 | 数据处理必备工具之 Pandas(基础篇) Python 教学 | 数据处理必备工具之 Pandas(数据的读取与导出) Python 教学 | Pandas 数据索引与数据选取 Python 教学 | Pandas 妙不可言的条件数据筛选 Python 教学 | Pandas 缺失值与重复值的处理方法 - Python 教学 | Pandas 表格数据行列变换 本期
星标⭐我们不迷路!想要文章及时到,文末“在看”少不了!
点击搜索你感兴趣的内容吧
往期推荐
Python 教学 | Pandas 表格数据行列变换
Python 教学 | Pandas 缺失值与重复值的处理方法
Python 教学 | Pandas 妙不可言的条件数据筛选
Python 教学 | Pandas 数据索引与数据选取
数据Seminar
这里是大数据、分析技术与学术研究的三叉路口
文 | 《社科领域大数据治理实务手册》
欢迎扫描👇二维码添加关注